<template>
  <n-form v-bind="getBindValue" :model="formModel" ref="formElRef">
    <n-grid v-bind="getGrid">
      <template v-for="schema in getSchema" :key="schema.field">
        <n-gi
          v-if="
            isNullOrUnDef(schema.isShow) ||
            schema.isShow === true ||
            (typeof schema.isShow !== 'boolean' && schema.isShow(formModel, schema) === true)
          "
          v-bind="schema.isTitle || schema.component === 'NTinymce' ? { span: 12 } : schema.giProps"
        >
          <div v-if="schema.isTitle" class="title">{{ schema.label }}</div>

          <n-form-item v-else :label="schema.label" :path="schema.field">
            <!--标签名右侧温馨提示-->
            <template #label v-if="schema.labelMessage">
              {{ schema.label }}
              <n-tooltip trigger="hover" :style="schema.labelMessageStyle">
                <template #trigger>
                  <n-icon size="18" class="cursor-pointer text-gray-400">
                    <QuestionCircleOutlined />
                  </n-icon>
                </template>
                {{ schema.labelMessage }}
              </n-tooltip>
            </template>
            <!--判断插槽-->

            <template v-if="schema.slot">
              <slot
                :name="schema.slot"
                :model="formModel"
                :field="schema.field"
                :value="formModel[schema.field]"
              ></slot>
            </template>
            <template v-else-if="schema.component === 'NTinymce'">
              <TinymceEditor
                class="TinymceEditor"
                v-model="formModel[schema.field]"
                :height="500"
                width="100%"
                v-bind="getComponentProps(schema)"
              />
            </template>
            <!--NCheckbox-->
            <template v-else-if="schema.component === 'NCheckbox'">
              <n-checkbox-group v-model:value="formModel[schema.field]">
                <n-space>
                  <n-checkbox
                    v-for="item in schema.componentProps.options"
                    :key="item.value"
                    :value="item.value"
                    :label="item.label"
                  />
                </n-space>
              </n-checkbox-group>
            </template>
            <!-- 编码生成器 -->
            <template v-else-if="schema.component === 'NCode'">
              <n-input-group>
                <n-input v-model:value="formModel[schema.field]" />
                <n-button
                  style="width: 50px; padding: 0 10px"
                  type="primary"
                  @click="formModel[schema.field] = generateRandomUpperCaseString(6)"
                >
                  生成
                </n-button>
              </n-input-group>
            </template>
            <!-- 文件与图片上传 -->
            <template
              v-else-if="schema.component === 'NUpload' || schema.component === 'NImgUpload'"
            >
              <MyUpload
                v-model="formModel[schema.field]"
                v-bind="getComponentProps(schema)"
                :is-img="schema.component === 'NImgUpload'"
              ></MyUpload>
            </template>
            <!--NRadioGroup-->
            <template v-else-if="schema.component === 'NRadioGroup'">
              <n-radio-group v-model:value="formModel[schema.field]">
                <n-space>
                  <n-radio
                    v-for="item in schema.componentProps.options"
                    :key="item.value"
                    :value="item.value"
                  >
                    {{ item.label }}
                  </n-radio>
                </n-space>
              </n-radio-group>
            </template>
            <!--NDatePicker-->
            <template v-else-if="schema.component === 'NDatePickerFormat'">
              <component
                v-bind="getComponentProps(schema)"
                :is="'NDatePicker'"
                v-model:formatted-value="formModel[schema.field]"
                :value-format="'yyyy-MM-dd HH:mm:ss'"
                :class="{ isFull: schema.isFull != false && getProps.isFull }"
              />
            </template>
            <!-- NTimeRange -->
            <template v-else-if="schema.component === 'NTimeRange'">
              <TimeRange v-model="formModel[schema.field]" v-bind="getComponentProps(schema)" />
            </template>
            <!-- SpaceEdit -->
            <template v-else-if="schema.component === 'SpaceEdit'">
              <ObjectEditBtn
                :modelValue="formModel[schema.field]"
                :id="formModel.id || ''"
                v-bind="getComponentProps(schema)"
                strong
                autoUpdate
                @autoUpdateData="(data) => Object.assign(formModel, data)"
              />
            </template>
            <!-- SpaceEdit -->
            <template v-else-if="schema.component === 'NAllSelect'">
              <AllSelect
                v-bind="getComponentProps(schema)"
                v-model="formModel[schema.field]"
                v-model:label="formModel[schema.moreField]"
              />
            </template>
            <!-- NAreaSelect -->
            <template v-else-if="schema.component === 'NAreaSelect'">
              <AreaSelect
                v-bind="getComponentProps(schema)"
                v-model="formModel[schema.field]"
                v-model:address="formModel[schema.moreField]"
              />
            </template>
            <!--动态渲染表单组件-->
            <component
              v-else
              v-bind="getComponentProps(schema)"
              :is="schema.component"
              v-model:value="formModel[schema.field]"
              :model="formModel"
              :field="schema.field"
              :class="{ isFull: schema.isFull != false && getProps.isFull }"
            />
            <!--组件后面的内容-->
            <template v-if="schema.suffix">
              <slot
                :name="schema.suffix"
                :model="formModel"
                :field="schema.field"
                :value="formModel[schema.field]"
              ></slot>
            </template>
          </n-form-item>
        </n-gi>
      </template>
      <!--提交 重置 展开 收起 按钮-->
      <n-gi
        :span="isInline ? '' : 24"
        :suffix="isInline ? true : false"
        #="{ overflow }"
        v-if="getProps.showActionButtonGroup"
      >
        <n-space
          align="center"
          :justify="isInline ? 'end' : 'start'"
          :style="{ 'margin-left': `${isInline ? 12 : getProps.labelWidth}px` }"
        >
          <n-button
            v-if="getProps.showSubmitButton"
            v-bind="getSubmitBtnOptions"
            @click="handleSubmit"
            :loading="loadingSub"
            >{{ getProps.submitButtonText }}
          </n-button>
          <n-button v-if="getProps.showResetButton" v-bind="getResetBtnOptions" @click="resetFields"
            >{{ getProps.resetButtonText }}
          </n-button>
          <n-button
            type="primary"
            text
            icon-placement="right"
            v-if="isInline && getProps.showAdvancedButton"
            @click="unfoldToggle"
          >
            <template #icon>
              <n-icon size="14" class="unfold-icon" v-if="overflow">
                <DownOutlined />
              </n-icon>
              <n-icon size="14" class="unfold-icon" v-else>
                <UpOutlined />
              </n-icon>
            </template>
            {{ overflow ? '展开' : '收起' }}
          </n-button>
        </n-space>
      </n-gi>
    </n-grid>
  </n-form>
</template>

<script lang="ts">
import type { Ref } from 'vue';
import { computed, defineComponent, onMounted, reactive, ref, unref, watch } from 'vue';
import { createPlaceholderMessage } from './helper';
import { useFormEvents } from './hooks/useFormEvents';
import { useFormValues } from './hooks/useFormValues';
import { basicProps } from './props';
import { DownOutlined, QuestionCircleOutlined, UpOutlined } from '@vicons/antd';
import type { GridProps } from 'naive-ui/lib/grid';
import type { FormActionType, FormProps, FormSchema } from './types/form';
import { isArray, isNullOrUnDef } from '@/utils/is';
import { deepMerge, generateRandomUpperCaseString } from '@/utils';
import { MyUpload } from '@/components/my-upload';
import ObjectEditBtn from '@/components/object-edit/object-edit-btn.vue';
import AreaSelect from '@/components/area-select/area-select.vue';
import TimeRange from '@/components/time-range/time-range.vue';
import { TinymceEditor } from '@/components/Tinymce';
import AllSelect from '@/components/all-select/all-select.vue';

export default defineComponent({
  name: 'BasicUpload',
  components: {
    DownOutlined,
    UpOutlined,
    QuestionCircleOutlined,
    MyUpload,
    ObjectEditBtn,
    AreaSelect,
    TimeRange,
    TinymceEditor,
    AllSelect,
  },
  props: {
    ...basicProps,
  },
  emits: ['reset', 'submit', 'register'],
  setup(props, { emit, attrs }) {
    const defaultFormModel = ref<Recordable>({});
    const formModel = reactive<Recordable>({});
    const propsRef = ref<Partial<FormProps>>({});
    const schemaRef = ref<Nullable<FormSchema[]>>(null);
    const formElRef = ref<Nullable<FormActionType>>(null);
    const gridCollapsed = ref(true);
    const loadingSub = ref(false);
    const isUpdateDefaultRef = ref(false);

    const getSubmitBtnOptions = computed(() => {
      return Object.assign(
        {
          size: props.size,
          type: 'primary',
        },
        props.submitButtonOptions
      );
    });

    const getResetBtnOptions = computed(() => {
      return Object.assign(
        {
          size: props.size,
          type: 'default',
        },
        props.resetButtonOptions
      );
    });

    function getComponentProps(schema) {
      const compProps = schema.componentProps ?? {};
      const customProps: Record<string, any> = {};
      switch (schema.component) {
        case 'NInput':
          Reflect.set(customProps, 'allow-input', isAllowInput);
          break;
        case 'NSelect':
          Reflect.set(customProps, 'fallback-option', false);
          break;
      }
      const component = schema.component;
      return {
        clearable: true,
        placeholder: createPlaceholderMessage(unref(component)),
        ...customProps,
        ...compProps,
      };
    }

    const getProps = computed((): FormProps => {
      const formProps = { ...props, ...unref(propsRef) } as FormProps;
      const rulesObj: any = {
        rules: {},
      };
      const schemas: any = formProps.schemas || [];
      schemas.forEach((item) => {
        if (item.rules && isArray(item.rules)) {
          rulesObj.rules[item.field] = item.rules;
        }
      });
      return { ...formProps, ...unref(rulesObj) };
    });

    const isInline = computed(() => {
      const { layout } = unref(getProps);
      return layout === 'inline';
    });

    const getGrid = computed((): GridProps => {
      const { gridProps } = unref(getProps);
      return {
        ...gridProps,
        collapsed: isInline.value ? gridCollapsed.value : false,
        responsive: 'screen',
      };
    });

    const getBindValue = computed(() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable));

    const getSchema = computed((): FormSchema[] => {
      const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
      for (const schema of schemas) {
        const { defaultValue } = schema;
        // handle date type
        // dateItemType.includes(component as string)
        if (defaultValue) {
          schema.defaultValue = defaultValue;
        }
      }
      return schemas as FormSchema[];
    });

    const { handleFormValues, initDefault } = useFormValues({
      getProps,
      defaultFormModel,
      getSchema,
      formModel,
    });

    const { handleSubmit, validate, resetFields, getFieldsValue, clearValidate, setFieldsValue } =
      useFormEvents({
        emit,
        getProps,
        formModel,
        getSchema,
        formElRef: formElRef as Ref<FormActionType>,
        defaultFormModel,
        loadingSub,
        handleFormValues,
      });

    function unfoldToggle() {
      gridCollapsed.value = !gridCollapsed.value;
    }

    async function setProps(formProps: Partial<FormProps>): Promise<void> {
      propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
    }

    const formActionType: Partial<FormActionType> = {
      getFieldsValue,
      setFieldsValue,
      resetFields,
      validate,
      clearValidate,
      setProps,
      submit: handleSubmit,
    };

    watch(
      () => getSchema.value,
      (schema) => {
        if (unref(isUpdateDefaultRef)) {
          return;
        }
        if (schema?.length) {
          initDefault();
          isUpdateDefaultRef.value = true;
        }
      }
    );

    onMounted(() => {
      initDefault();
      emit('register', formActionType);
    });

    function isTimeformat(schema: FormSchema) {
      if (getComponentProps(schema)) {
        const type = getComponentProps(schema).type;
        if (type && type?.includes('time')) {
          return 'yyyy-MM-dd HH:mm:ss';
        }
      }
      return 'yyyy-MM-dd';
    }

    const isAllowInput = (val: string) => {
      if (val && typeof val === 'string' && /^\s+$/g.test(val)) return false;
      return true;
    };
    return {
      formElRef,
      formModel,
      getGrid,
      getProps,
      getBindValue,
      getSchema,
      getSubmitBtnOptions,
      getResetBtnOptions,
      handleSubmit,
      resetFields,
      loadingSub,
      isInline,
      getComponentProps,
      unfoldToggle,
      isTimeformat,
      generateRandomUpperCaseString,
      isNullOrUnDef,
      isAllowInput,

      uselessVal: ref(),
    };
  },
});
</script>

<style lang="less" scoped>
.isFull {
  width: 100%;
  justify-content: flex-start;
}

.title {
  color: #1f2225;
  text-align: left;
  height: 30px;
  line-height: 30px;
  font-size: 15px;
  font-weight: bold;
  margin-bottom: 5px;
}

.unfold-icon {
  display: flex;
  align-items: center;
  height: 100%;
  margin-left: -3px;
}

.TinymceEditor {
  height: 100px;
}
</style>
